﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CarHelpDesk.Web.Utils;
using System.Transactions;

namespace CarHelpDesk.Web.Models.Repositories.LinqRepositories
{
    public class AnswersRepository : IAnswersRepository
    {        
        #region Constants

        //TODO: store constants in DB
        public static readonly int CORRECT_ANSWER_VALUE = 15;
        public static readonly int ANSWER_VOTE_UP_VALUE = 10;
        public static readonly int ANSWER_VOTE_DOWN_VALUE = -2;

        #endregion

        private LinqModelDataContext _db;
        private UserDetailsRepository userRepo;

        public AnswersRepository()
        {
            _db = new LinqModelDataContext();
#if DEBUG
            _db.Log = DebugTextWriter.Instance;
#endif
            this.userRepo = new UserDetailsRepository(this._db);
        }
        public AnswersRepository(bool isTest)
        {
            if (isTest)
                _db = new LinqModelDataContext(CarHelpDesk.Web.Models.Properties.Settings.Default.db_helpdesk_testConnectionString);
            else
                _db = new LinqModelDataContext();
#if DEBUG
            _db.Log = DebugTextWriter.Instance;
#endif
            this.userRepo = new UserDetailsRepository(this._db);
        }

        #region IAnswersRepository Members

        /// <summary>
        /// Saves answer to the database
        /// </summary>
        /// <param name="answer">The answer.</param>
        public void Create(Answer answer)
        {
            _db.Answers.InsertOnSubmit(answer);
            _db.SubmitChanges();
        }

        /// <summary>
        /// Retrieves all answers given to specified Question
        /// </summary>
        /// <param name="questionID">valid question identificator</param>
        /// <returns>IQueryable of Answers</returns>
        public IQueryable<Answer> getAnswersByQuestionID(int questionID)
        {
            return from p in _db.Answers
                   where p.QuestionID == questionID
                   select p;
        }

        /// <summary>
        /// Get all answers' IDs associated with an existing question.
        /// </summary>
        /// <param name="questionID"></param>
        /// <returns></returns>
        public List<int> getAnswersIDsByQuestionID(int questionID)
        {
            return (from p in _db.Answers
                    where p.QuestionID == questionID
                    select p.ID).ToList();
        }

        /// <summary>
        /// Delete all answers associated with an existing question.
        /// </summary>
        /// <param name="questionID"></param>
        public void DeleteAnswersByQuestionID(int questionID)
        {
            List<Answer> answers = getAnswersByQuestionID(questionID).ToList();
            foreach (Answer answer in answers)
            {
                _db.Answers.DeleteOnSubmit(answer);                
            }
            _db.SubmitChanges();
        }

        /// <summary>
        /// Retrieves the answer by ID.
        /// </summary>
        /// <param name="answerID">The valid answer ID.</param>
        /// <returns></returns>
        public Answer getAnswerByID(int answerID)
        {
            return this._db.Answers.FirstOrDefault(a => a.ID == answerID);
        }

        /// <summary>
        /// Updates the specified answer
        /// </summary>
        /// <param name="answer"></param>
        public void Update(Answer answer)
        {
            _db.SubmitChanges();
        }

        /// <summary>
        /// Marks answer as accepted.
        /// </summary>
        /// <param name="answer">The answer.</param>
        public void MarkAnswerAsCorrect(Answer answer)
        {
            using (var transaction = new TransactionScope())
            {
                answer.IsCorrect = true;

                this.userRepo.addToUserRating(answer.aspnet_User, CORRECT_ANSWER_VALUE);

                _db.SubmitChanges();
                 
                transaction.Complete();
            }
        }


        /// <summary>
        /// Gets AnswerVote for user.
        /// </summary>
        /// <param name="answer">The answer.</param>
        /// <param name="userID">The user ID.</param>
        /// <returns>The AnswerVote or null if there is none</returns>
        public AnswerVote GetAnswerVote(Answer answer, Guid userID)
        {
            return answer.AnswerVotes.FirstOrDefault(vote => vote.UserID == userID);
        }


        /// <summary>
        /// Creates an AnswerVote.
        /// </summary>
        /// <param name="vote">The vote.</param>
        public void CreateAnswerVote(AnswerVote vote)
        {
            if (vote.VoteValue != 1 && vote.VoteValue != -1)
                throw new ArgumentException("VoteValue should be 1 or -1", "VoteValue");

            using (var transaction = new TransactionScope())
            {
                Answer answer = this._db.Answers.First(a => a.ID == vote.AnswerID);
                answer.AnswerVotes.Add(vote);

                if (vote.VoteValue == 1)
                {
                    answer.VoteUpCount++;
                    this.userRepo.addToUserRating(answer.aspnet_User, ANSWER_VOTE_UP_VALUE);
                }
                else
                {
                    answer.VoteDownCount++;
                    this.userRepo.addToUserRating(answer.aspnet_User, ANSWER_VOTE_DOWN_VALUE);
                }

                _db.SubmitChanges();
                transaction.Complete();
            }
        }

        /// <summary>
        /// Delete all answer votes associated with an existing answer.
        /// </summary>
        /// <param name="answerID"></param>
        public void DeleteAnswerVoteByAnswerID(int answerID)
        {
            List<AnswerVote> answerVotes = (from p in _db.AnswerVotes
                                            where p.AnswerID == answerID
                                            select p).ToList();
            foreach (AnswerVote answerVote in answerVotes)
            {
                _db.AnswerVotes.DeleteOnSubmit(answerVote);
            }

            _db.SubmitChanges();
        }

        #endregion
    }
}
